/* Author: Mark Faust;   
 * Description: This file defines the two required functions for the branch predictor.
 */

#include "predictor.h"
#include <cstdlib>
#include <iostream>


int LocalHistory[ LOCAL_SIZE ] = {0}; //initialize all elemtns to 0
int LocalPrediction[ LOCAL_SIZE ] = {0};
int GlobalPrediction[ GLOBAL_SIZE ] = {0};
int GlobalChoice[ GLOBAL_SIZE ] = {0};

//int BranchHistory = 0;
unsigned int PC = 0;



int Perceptrons[ NUM_PERCEPTRONS ][ NUM_WEIGHTS ] = { 0 };
int BranchHistory[ HISTORY_LENGTH ] = { 0 };
//int BranchHistory[ HISTORY_LENGTH ] = { 1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1 };

unsigned branch_address = 0;
int index_p = 0;
int prediction = 0;
unsigned global_history = 0;
unsigned global_history_p = 0;

using namespace std;

bool PREDICTOR::get_prediction( const branch_record_c* br, const op_state_c* os )
{
	int GlobalC;
	int GlobalP;

		    branch_address = br->instruction_addr;

			PC = br->instruction_addr;
			PC = PC & PC_MASK;		//select 10 bit for use in branch predictor
			PC = PC >> 2;

			GlobalC = GlobalChoice[global_history];


			
			GlobalP = GlobalPrediction[global_history];

		    index_p = getPerceptronIndex();
		    
		    prediction = getPrediction(); 			



			if ((GlobalC & GLOBAL_MASK) == 0x02) //if thepredictor choice is 1 -> use the global predictor
			{
				
				//GlobalP = GlobalPrediction[global_history];

				if ((GlobalP & GLOBAL_MASK) == 0x02)
					return TAKEN;
				else
					return NOT_TAKEN;
			}
			
			else				//else ethe
			{
			    
				    //index_p = getPerceptronIndex();
				    
				    //prediction = getPrediction(); 
					    
				    if( prediction >= 0 )
					return TAKEN;
				    else
					return NOT_TAKEN;
			}

}    
 
 
// Update the predictor after a prediction has been made.  This should accept
// the branch record (br) and architectural state (os), as well as a third
// argument (taken) indicating whether or not the branch was taken.
void PREDICTOR::update_predictor( const branch_record_c* br, const op_state_c* os, bool taken )
{
			int LocalH;
			int LocalP;
			int GlobalC;
			int GlobalP;
			
			bool LP;
			bool GP;

			LocalH = LocalHistory[ PC ];
			LocalP = LocalPrediction[ LocalH ];

			GlobalC = GlobalChoice[ global_history ];
			GlobalP = GlobalPrediction[ global_history ];

			// Convert the local and global predictions to boolean values.

			if( LocalP & LP_MSB_A_1 )
				LP = TAKEN;
			else
				LP = NOT_TAKEN;
				
			if( GlobalP & GP_MSB_A_1 )
				GP = TAKEN;
			else
				GP = NOT_TAKEN;

    bool prediction_sign;
    int t;

    
    if( prediction >= 0 )
{
	LP = TAKEN;
        prediction_sign = true;
}
    else
{
	LP = NOT_TAKEN;
        prediction_sign = false;
}

    //if( br->is_conditional )
	   global_history_p <<= 1;

    if( taken )
    {
	   // if( br->is_conditional )
	    global_history_p |= GLOBAL_HISTORY_UPDATE_T;
        t = 1;
				switch( LocalP )
				{
					case LT_STRONGLY: 		break;	// Predictor bits saturated.
					
					case LT_LESS_STRONGLY:	LocalPrediction[ LocalH ] = LT_STRONGLY;
											break;
					
					case LT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LT_LESS_STRONGLY;
											break;
					
					case LT_WEAKLY: 		LocalPrediction[ LocalH ] = LT_LESS_WEAKLY;
											break;
					
					case LNT_WEAKLY: 		LocalPrediction[ LocalH ] = LT_WEAKLY;
											break;
					
					case LNT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LNT_WEAKLY;
											break;
					
					case LNT_LESS_STRONGLY: LocalPrediction[ LocalH ] = LNT_LESS_WEAKLY;
											break;
					
					case LNT_STRONGLY: 		LocalPrediction[ LocalH ] = LNT_LESS_STRONGLY;
											break;
					
					default: 				cerr << "Predictor Bits out of Range!!!" << endl;
											break;
				}

				switch( GlobalP )
				{
					case GT_STRONGLY: 	break;	// Predictor bits saturated.	
					
					case GT_WEAKLY:		GlobalPrediction[ global_history ] = GT_STRONGLY;
										break;
					
					case GNT_WEAKLY: 	GlobalPrediction[ global_history ] = GT_WEAKLY;
										break;
					
					case GNT_STRONGLY:	GlobalPrediction[ global_history ] = GNT_WEAKLY;
										break;
					
					default: 			cerr << "Predictor Bits out of Range!!!" << endl;
										break;
				}
    }
    else
    {	        
	//if( br->is_conditional )
	    global_history_p &= GLOBAL_HISTORY_UPDATE_N;
        t = -1;

				switch( LocalP )
				{
					case LT_STRONGLY: 		LocalPrediction[ LocalH ] = LT_LESS_STRONGLY;
											break;
					
					case LT_LESS_STRONGLY: 	LocalPrediction[ LocalH ] = LT_LESS_WEAKLY;
											break;
					
					case LT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LT_WEAKLY;
											break;
					
					case LT_WEAKLY: 		LocalPrediction[ LocalH ] = LNT_WEAKLY;
											break;
					
					case LNT_WEAKLY: 		LocalPrediction[ LocalH ] = LNT_LESS_WEAKLY;
											break;
					
					case LNT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LNT_LESS_STRONGLY;
											break;
					
					case LNT_LESS_STRONGLY: LocalPrediction[ LocalH ] = LNT_STRONGLY;
											break;
					
					case LNT_STRONGLY: 		break;	// Predictor bits saturated.
					
					default: 				cerr << "Predictor Bits out of Range!!!" << endl;
											break;
				}

				switch( GlobalP )
				{
					case GT_STRONGLY: 	GlobalPrediction[ global_history ] = GT_WEAKLY;
										break;	
					
					case GT_WEAKLY:		GlobalPrediction[ global_history ] = GNT_WEAKLY;
										break;
					
					case GNT_WEAKLY: 	GlobalPrediction[ global_history ] = GNT_STRONGLY;
										break;
					
					case GNT_STRONGLY: 	break;	// Predictor bits saturated.
					
					default: 			cerr << "Predictor Bits out of Range!!!" << endl;
										break;
				}
    }

	    global_history &= GLOBAL_HISTORY_MASK;

			// If the local and global predictions are different, then train the global choice
			// to prefer the correct prediction.  If the local and global predictions are the
			// same, no learning is done.
			if( GP != LP )
			{
				if( GP == taken )
				{
					switch( GlobalC )
					{
						case GT_STRONGLY:	break;	// Predictor bits saturated.
						
						case GT_WEAKLY:		GlobalChoice[ global_history ] = GT_STRONGLY;
											break;

						case GNT_WEAKLY: 	GlobalChoice[ global_history ] = GT_WEAKLY;
											break;
						
						case GNT_STRONGLY: 	GlobalChoice[ global_history ] = GNT_WEAKLY;
											break;
						
						default: 			cerr << "Predictor Bits out of Range!!!" << endl;
											break;
					}
				}
				else // LP == taken
				{
					switch( GlobalC )
					{
						case GT_STRONGLY: 	GlobalChoice[ global_history ] = GT_WEAKLY;
											break;	
						
						case GT_WEAKLY: 	GlobalChoice[ global_history ] = GNT_WEAKLY;
											break;
						
						case GNT_WEAKLY: 	GlobalChoice[ global_history ] = GNT_STRONGLY;
											break;
						
						case GNT_STRONGLY: 	break;
						
						default: 			cerr << "Predictor Bits out of Range!!!" << endl;
											break;
					}
				}
			}

			// Update the branch history with the actual outcome of the branch.
			if( taken )
			{
				global_history = global_history << 1; 
				global_history = global_history & BRANCH_MASK;
				global_history = global_history | BRANCH_TAKEN;
			}
			else // Branch not taken.
			{
				global_history = global_history << 1; 
				global_history = global_history & BRANCH_MASK;
			}
			
			// Update the local history with the actual outcome of the branch.
			if( taken )
			{
				LocalHistory[ PC ] = LocalHistory[ PC ] << 1;
				LocalHistory[ PC ] = LocalHistory[ PC ] & LOCAL_HISTORY_MASK;
				LocalHistory[ PC ] = LocalHistory[ PC ] | BRANCH_TAKEN;
			}
			else // Branch not taken.
			{
				LocalHistory[ PC ] = LocalHistory[ PC ] << 1;
				LocalHistory[ PC ] = LocalHistory[ PC ] & LOCAL_HISTORY_MASK;
			}

	    
    if( ( prediction_sign != taken ) ||  ( abs( prediction ) <= PHI ) )
    {
        for( int i = 0; i < NUM_WEIGHTS; ++i )
            Perceptrons[ index_p ][ i ] = Perceptrons[ index_p ][ i ] + ( t * BranchHistory[ i ] );
    }
    
    for( int i = 0; i < HISTORY_LENGTH - 1; ++i )
        BranchHistory[ i ] = BranchHistory[ i + 1 ];
        
    BranchHistory[ HISTORY_LENGTH - 1 ] = t;

    /* replace this code with your own */
    /*    bool prediction = false;

    printf("%0x %0x %1d %1d %1d %1d ",br->instruction_addr,
                                      br->branch_target,br->is_indirect,br->is_conditional,
                                      br->is_call,br->is_return);
    if( br->is_conditional )
        prediction = true;
    return prediction;   // true for taken, false for not taken
    */
}

int getPerceptronIndex()
{
	
	unsigned int global_historyu = global_history_p << 16 ;
	global_historyu &= ~GLOBAL_HISTORY_MASK;

	//unsigned int branch_addressu = (branch_address*PRIME_NUM) & ~GLOBAL_HISTORY_MASK;
	unsigned int branch_addressu = (branch_address) & ~GLOBAL_HISTORY_MASK;
	branch_addressu ^= global_historyu;

	unsigned int branch_addressl = branch_address & GLOBAL_HISTORY_MASK;
	branch_addressl ^= global_history;

	/*
	unsigned int global_historyu = global_history << 12 ;
	global_historyu &= ~GLOBAL_HISTORY_MASK;

	unsigned int branch_address_retainu = global_history & GLOBAL_HISTORY_MASKRU;
       	unsigned int branch_address_retainl = global_history & GLOBAL_HISTORY_MASKRL;

	unsigned int branch_addressu = (branch_address*PRIME_NUM) & ~GLOBAL_HISTORY_MASK;
	branch_addressu ^= global_historyu;
	branch_addressu &= ~GLOBAL_HISTORY_MASKRU;

	unsigned int branch_addressl = branch_address & GLOBAL_HISTORY_MASK;
	branch_addressl ^= global_history;
	branch_addressl &= ~GLOBAL_HISTORY_MASKRL;
	*/
/*
    unsigned long int mask = 0x3FF;
    unsigned long int PC_31to10 = branch_address >> 10;
    unsigned long int PC_9to2 = (( branch_address >> 2 ) & mask );
    
    return (( PRIME_NUM * PC_31to10 ) + PC_9to2 ) % NUM_PERCEPTRONS;
*/
	//return abs(branch_address % NUM_PERCEPTRONS);
	//return branch_address % NUM_PERCEPTRONS;

	return ((branch_addressu | branch_addressl) % NUM_PERCEPTRONS);

	//return ((branch_addressu | branch_address_retainu | branch_address_retainl | branch_addressl) % NUM_PERCEPTRONS);
}

int getPrediction()
{
    int prediction_guess = 0;
    
    for( int i = 1; i < NUM_WEIGHTS; ++i )
        prediction_guess = prediction_guess + ( BranchHistory[ i ] * Perceptrons[ index_p ][ i ] );
        
    prediction_guess = prediction_guess + Perceptrons[ index_p ][ 0 ];
    
    return prediction_guess;
}
